//
//  TMRefresh.m
//  cutter
//
//  Created by mac on 15/5/23.
//  Copyright © 2015年 e360. All rights reserved.
//

#import "TMRefresh.h"

@interface TMRefresh ()
@property(nonatomic, strong) UILabel *title;
@property(nonatomic, strong) UIActivityIndicatorView *indicator;
@property(nonatomic, readonly) UIScrollView *scrollView;
//
@property(nonatomic, strong) NSMutableDictionary *titles;
@property(nonatomic, assign) UIEdgeInsets refreshInset;
@property(nonatomic, assign) UIEdgeInsets scrollInset;
@property(nonatomic, assign) CGPoint scrollOffset;
//
@property(nonatomic, assign) TMRefreshType type;
@property(nonatomic, assign) NSInteger current;
@property(nonatomic, assign) BOOL isObserver;
@property(nonatomic, assign) BOOL refresh;
@end
@implementation TMRefresh
- (instancetype)initWithType:(TMRefreshType)type{
    self = [super initWithFrame:CGRectZero];
    if (self) {
        [self setType:type];
        [self setUserInteractionEnabled:NO];
        //
        [self setIndicator:[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]];
        [self.indicator setHidesWhenStopped:NO];
        [self addSubview:self.indicator];
        //
        [self setTitle:[[UILabel alloc] initWithFrame:CGRectZero]];
        [self.title setFont:[UIFont boldSystemFontOfSize:10]];
        [self.title setTextColor:[UIColor darkGrayColor]];
        [self addSubview:self.title];
    }
    return self;
}
- (void)willMoveToWindow:(UIWindow *)newWindow{
    [super willMoveToWindow:newWindow];
    //
    if (self.scrollView) {
        if (newWindow == nil) {
            [self removeObserver:self.scrollView];
        }else{
            [self addObserver:self.scrollView];
        }
    }
}
- (void)dealloc{
    NSLog(@"TMRefresh dealloc");
}
- (void)updateConstraints{
    [super updateConstraints];
    [self layoutSubviews];
}
- (void)layoutSubviews{
    [super layoutSubviews];
    //
    if (self.scrollView && self.hidden==NO) {
        static CGFloat height = 60.f;
        if (self.type == TMRefreshTypeHeader) {
            [self setFrame:CGRectMake(0, 0 - height, self.scrollView.bounds.size.width, height)];
        }else{
            [self setFrame:CGRectMake(0, MAX(self.scrollView.bounds.size.height, self.scrollView.contentSize.height), self.scrollView.bounds.size.width, height)];
        }
        //
        CGFloat ih = self.indicator.bounds.size.height * 0.5 + 2.0;
        CGFloat th = self.title.bounds.size.height * 0.5 + 2.0;
        CGFloat cx = self.scrollView.bounds.size.width * 0.5;
        CGFloat cy = height * 0.5;
        CGFloat iy = cy - ih;
        CGFloat ty = cy + th;
        //
        [self.indicator setCenter:CGPointMake(cx, iy)];
        [self.title setCenter:CGPointMake(cx, ty)];
    }
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentSize"] || [keyPath isEqualToString:@"bounds"]){
        [self setNeedsLayout];
    }
    //
    if (self.isRefreshing || (self.type == TMRefreshTypeFooter && self.finish)) {
        return;
    }
    //
    CGFloat ty = 0;
    if (self.type == TMRefreshTypeHeader) {
        ty = 0 - self.scrollView.contentOffset.y;
    }else{
        ty = self.scrollView.contentOffset.y - MAX(self.scrollView.contentSize.height - self.scrollView.frame.size.height, 0);
    }
    if (ty >= 0) {
        ty = MIN(1, ty / self.bounds.size.height);
        if (ty < 1) {
            [self.indicator setTransform:CGAffineTransformMakeRotation(ty * M_PI * 2)];
            [self updataTitle:TMRefreshStatePulling];
        }else{
            if ([keyPath isEqualToString:@"state"]) {
                if (self.scrollView.panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
                    [self beginRefreshing];
                    [self sendActionsForControlEvents:UIControlEventValueChanged];
                }
            }else{
                if (self.scrollView.isTracking && self.scrollView.isDragging) {
                    [self.indicator setTransform:CGAffineTransformMakeRotation(ty * M_PI * 2)];
                    [self updataTitle:TMRefreshStateNormal];
                }
            }
        }
    }
}
//
- (UIScrollView *)scrollView{
    UIView *view = [self superview];
    if ([view isKindOfClass:UIScrollView.class]) {
        return (id)view;
    }
    return nil;
}
- (BOOL)isRefreshing{
    return self.refresh;
}
- (void)updataTitle:(TMRefreshState)state{
    if (self.current != state) {
        [self setCurrent:state];
        //
        NSString *key = [NSString stringWithFormat:@"%lu",(unsigned long)state];
        if ([self.titles objectForKey:key]) {
            [self.title setText:[self.titles objectForKey:key]];
            [self.title sizeToFit];
        }
        //
        if (self.current == TMRefreshStateRefreshing) {
            [self.indicator startAnimating];
        }else{
            [self.indicator stopAnimating];
        }
        //
        if (self.type == TMRefreshTypeFooter) {
            [self setHidden:self.finish];
        }
        [self layoutIfNeeded];
    }
}
-(void)beginRefreshing{
    __weak typeof(self) this = self;
    [self execute:[NSOperationQueue mainQueue] block:^{
        if (this.refresh == NO) {
            [this setRefresh:YES];
            [this.scrollView layoutIfNeeded];
            [this updataTitle:TMRefreshStateRefreshing];
            [this setRefreshInset:self.scrollView.contentInset];
            //
            if (this.type == TMRefreshTypeHeader) {
                [this setScrollInset:UIEdgeInsetsMake(this.refreshInset.top + this.bounds.size.height, this.refreshInset.left, this.refreshInset.bottom, this.refreshInset.right)];
                [this setScrollOffset:CGPointMake(this.scrollInset.left, 0 - this.scrollInset.top)];
            }else{
                [this setScrollInset:UIEdgeInsetsMake(this.refreshInset.top, this.refreshInset.left, this.refreshInset.bottom + this.bounds.size.height, this.refreshInset.right)];
                [this setScrollOffset:CGPointMake(this.scrollInset.left, this.scrollInset.bottom + MAX(this.scrollView.contentSize.height - this.scrollView.frame.size.height, 0))];
            }
            //
            [this.scrollView setScrollEnabled:NO];
            [UIView beginAnimations:nil context:nil];
            [this.scrollView setContentInset:self.scrollInset];
            [this.scrollView setContentOffset:self.scrollOffset animated:NO];
            [UIView commitAnimations];
        }
    }];
}
-(void)endRefreshing{
    __weak typeof(self) this = self;
    [self execute:[NSOperationQueue mainQueue] block:^{
        if (this.refresh == YES) {
            [this setRefresh:NO];
            [this updataTitle:TMRefreshStateNormal];
            //
            if (this.type == TMRefreshTypeHeader) {
                [this setScrollOffset:CGPointMake(this.refreshInset.left, 0 - this.refreshInset.top)];
            }else{
                if (this.finish) {
                    [this setScrollOffset:CGPointMake(this.refreshInset.left, this.refreshInset.bottom + MAX(this.scrollView.contentSize.height - this.scrollView.frame.size.height, 0))];
                }else{
                    [this setScrollOffset:CGPointMake(this.scrollInset.left, this.scrollInset.bottom + MAX(this.scrollView.contentSize.height - this.scrollView.frame.size.height, 0))];
                }
            }
            //
            [NSThread sleepForTimeInterval:0.1];
            [this.scrollView setScrollEnabled:YES];
            [UIView beginAnimations:nil context:nil];
            [this.scrollView setContentInset:this.refreshInset];
            [this.scrollView setContentOffset:this.scrollOffset animated:NO];
            [UIView commitAnimations];
        }
    }];
}
//
-(void)execute:(NSOperationQueue*)queue block:(void (^)(void))block{
    if ([NSOperationQueue currentQueue] != queue) {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:block];
        [queue addOperation:operation];
        [operation waitUntilFinished];
    }else{
        block();
    }
}
- (void)addObserver:(UIScrollView*)scrollView{
    if (scrollView && self.isObserver == NO) {
        [self setIsObserver:YES];
        [scrollView.panGestureRecognizer addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil];
        [scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
        [scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
        [scrollView addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew context:nil];
    }
}
- (void)removeObserver:(UIScrollView*)scrollView{
    if (scrollView && self.isObserver == YES) {
        [self setIsObserver:NO];
        [scrollView.panGestureRecognizer removeObserver:self forKeyPath:@"state"];
        [scrollView removeObserver:self forKeyPath:@"contentOffset"];
        [scrollView removeObserver:self forKeyPath:@"contentSize"];
        [scrollView removeObserver:self forKeyPath:@"bounds"];
    }
}
- (void)setTitle:(NSString *)title forState:(TMRefreshState)state{
    NSString *key = [NSString stringWithFormat:@"%lu",(unsigned long)state];
    [self.titles setValue:title forKey:key];
    [self updataTitle:self.current];
}
- (NSMutableDictionary *)titles{
    if (_titles == nil) {
        [self setCurrent:TMRefreshStateNull];
        [self setTitles:[NSMutableDictionary dictionary]];
    }
    return _titles;
}
@end

//
@implementation UIScrollView (TMRefresh)
@dynamic headerView,footerView;
-(TMRefresh *)headerView{
    for (TMRefresh *refresh in self.subviews) {
        if ([refresh isKindOfClass:TMRefresh.class] && refresh.type==TMRefreshTypeHeader) {
            return refresh;
        }
    }
    TMRefresh *refresh = [[TMRefresh alloc] initWithType:TMRefreshTypeHeader];
    [self addSubview:refresh];
    return refresh;
}
-(TMRefresh *)footerView{
    for (TMRefresh *refresh in self.subviews) {
        if ([refresh isKindOfClass:TMRefresh.class] && refresh.type==TMRefreshTypeFooter) {
            return refresh;
        }
    }
    TMRefresh *refresh = [[TMRefresh alloc] initWithType:TMRefreshTypeFooter];
    [self addSubview:refresh];
    return refresh;
}
@end
